﻿using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using élénkPróbája.extentions;
using élénkPróbája.Test.dto;
using élénkPróbája.Test.dto.@interface;
using élénkPróbája.Test.extention;
using élénkPróbája.Test.helpers;
using élénkPróbája.validators;

namespace élénkPróbája.Test
{
    [TestClass]
    public class CollectionValidatorTests
    {
        Company _company;
        public CollectionValidatorTests()
        {
            _company = new Company()
            {
                Accounts = new List<Account>() {
                    new Account() { AccountName = "Personal"},
                    new Account { Balance = 100}
                }
            };
        }

        [TestMethod]
        public void Validates_collection()
        {
            var validator = new TestValidator {
                v => v.RuleFor(x => x.Name).NotNull(),
                v => v.RuleFor(x => x.Accounts).SetCollectionValidator(new AccountValidator())
            };

            var results = validator.Validate(_company);
            results.Errors.Count.ShouldEqual(3);

            results.Errors[0].PropertyName.ShouldEqual("Name");
            results.Errors[1].PropertyName.ShouldEqual("Accounts[0].Balance");
            results.Errors[2].PropertyName.ShouldEqual("Accounts[1].AccountName");
        }
        [TestMethod]
        public void Collection_should_be_explicitly_included_with_expression()
        {
            var validator = new TestValidator {
                v => v.RuleFor(x => x.Name).NotNull(),
                v => v.RuleFor(x => x.Accounts).SetCollectionValidator(new AccountValidator())
            };

            var results = validator.Validate(_company, x => x.Accounts);
            results.Errors.Count.ShouldEqual(2);
        }
        [TestMethod]
        public void Collection_should_be_explicitly_included_with_string()
        {
            var validator = new TestValidator {
                v => v.RuleFor(x => x.Name).NotNull(),
                v => v.RuleFor(x => x.Accounts).SetCollectionValidator(new AccountValidator())
            };

            var results = validator.Validate(_company, "Accounts");
            results.Errors.Count.ShouldEqual(2);
        }
        [TestMethod]
        public void Collection_should_be_excluded()
        {
            var validator = new TestValidator {
                v => v.RuleFor(x => x.Name).NotNull(),
                v => v.RuleFor(x => x.Accounts).SetCollectionValidator(new AccountValidator())
            };

        var results = validator.Validate(_company, x => x.Name);
            results.Errors.Count.ShouldEqual(1);
        }
        [TestMethod]
        public void Condition_should_work_with_child_collection()
        {
            var validator = new TestValidator() {
                v => v.RuleFor(x => x.Accounts).SetCollectionValidator(new AccountValidator()).When(x => x.Accounts.Count == 3 /*there are only 2*/)
            };

            var result = validator.Validate(_company);
            result.IsValid.ShouldBeTrue();
        }
        [TestMethod]
        public void Async_condition_should_work_with_child_collection()
        {
            var validator = new TestValidator() {
                                                    v => v.RuleFor(x => x.Accounts).SetCollectionValidator(new AccountValidator()).WhenAsync(x => hTask.FromResult(x.Accounts.Count == 3 /*there are only 2*/))
                                                };

            var result = validator.ValidateAsync(_company).Result;
            result.IsValid.ShouldBeTrue();
        }
        [TestMethod]
        public void Skips_null_items()
        {
            var validator = new TestValidator {
                v => v.RuleFor(x => x.Name).NotNull(),
                v => v.RuleFor(x => x.Accounts).SetCollectionValidator(new AccountValidator())
            };

            _company.Accounts[0] = null;
            var results = validator.Validate(_company);
            results.Errors.Count.ShouldEqual(2); //2 errors - 1 for person, 1 for 2nd Order.
        }
        [TestMethod]
        public void Can_validate_collection_using_validator_for_base_type()
        {
            var validator = new TestValidator() {
                v => v.RuleFor(x => x.Accounts).SetCollectionValidator(new AccountInterfaceValidator())
            };

            var result = validator.Validate(_company);
            result.IsValid.ShouldBeFalse();
        }
        [TestMethod]
        public void Can_specifiy_condition_for_individual_collection_elements()
        {
            var validator = new TestValidator {
                v => v.RuleFor(x => x.Accounts)
                    .SetCollectionValidator(new AccountValidator())
                    .Where(x => x.AccountName != null)
            };

            var results = validator.Validate(_company);
            results.Errors.Count.ShouldEqual(1);
        }
        [TestMethod]
        public void Should_override_property_name()
        {
            var validator = new TestValidator {
                v => v.RuleFor(x => x.Accounts).SetCollectionValidator(new AccountValidator())
                    .OverridePropertyName("Accounts2")
            };

            var results = validator.Validate(_company);
            results.Errors[0].PropertyName.ShouldEqual("Accounts2[0].Balance");
        }

        #region InternalClasses
        public class AccountValidator : AbstractValidator<Account>
        {
            public AccountValidator()
            {
                RuleFor(x => x.AccountName).NotEmpty();
                RuleFor(x => x.Balance).NotEqual(0);
            }
        }

        public class AccountInterfaceValidator : AbstractValidator<IAccount>
        {
            public AccountInterfaceValidator()
            {
                RuleFor(x => x.Balance).NotEqual(0);
            }
        }

        #endregion
    }
}
